// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.


#import "cocos2d.h"
#import "PuzzleCommon.h"

bool isTouchInSprite(UITouch* touch, CCSprite* sprite);

// Winning particle emitter.
@interface CCParticleWinRain : CCQuadParticleSystem
{
}
@end

// Hack to clip blocks using GL clipping on top of the stack
@interface TopClippedSprite : CCSprite {
	@public
	GridConfig* g_;

	bool clipped_;
}

- (id) initInGrid:(GridConfig*)grid;
- (id) initInGrid:(GridConfig*)grid file:(NSString*)file;
- (void) visit;
@end

// Hack to clip the speed selector
@interface SpeedClippedNode : CCNode {
}

- (id) init;
- (void) visit;
@end

// Convenience actions
@interface SetTextureAction : CCInstantAction
{
	CCTexture2D* texture;
}
+(id) actionWithTexture: (CCTexture2D*) tex;
-(id) initWithTexture: (CCTexture2D*) tex;
@end

// Should be applied on Block objects only.
@interface MeltBlockAction : CCInstantAction
{
}
+(id) action;
-(id) init;
@end

@interface KillNodeAction : CCInstantAction
{
}
+(id) action;
-(id) init;
@end

@class TrashIcon;

// A single block on the game board.  Also used in a restricted way for blocks that aren't yet on the board,
// like those in the stack below the screen and the next blocks.
@interface Block : TopClippedSprite {
	CGPoint gridpos;  // Position in grid.  One tile corresponds to one unit in this coordinate system.
	BlockColor col;
	BlockShape shape;
	
	bool isbeingthrown;  // throw animation in progress
	bool ishanging;  // blocks that just were placed or became unsupported hang in the air for a bit
	bool isfalling;  // is this block not landed?
	bool ismelting;  // is this block in the middle of a melting animation?
	bool neverlanded;  // has this block just been thrown by the player/trash and never touched the stack?

	ccTime melttime;  // time left before it finishes melting, for matched blocks
	ccTime falldelay;  // time left before it starts falling, for 'hanging' blocks

	int chaingroup;  // number identifying chain/combo this is a member of
	int chaincount;  // chain counter.  see comment in 'testMatches'


	@public
	CCSprite* heart1;
	CCSprite* heart2;
	
	CCSprite* tutorialhand;

	TrashIcon* trashicon;  // corresponding icon, for trash blocks only

	CCParticleFlower* emitter[2];
}

@property (nonatomic,readwrite) CGPoint gridpos;
@property (nonatomic,readwrite) BlockColor col;
@property (nonatomic,readwrite) BlockShape shape;
@property (nonatomic,readwrite) bool isbeingthrown;
@property (nonatomic,readwrite) bool ishanging;
@property (nonatomic,readwrite) bool isfalling;
@property (nonatomic,readwrite) bool ismelting;
@property (nonatomic,readwrite) bool neverlanded;
@property (nonatomic,readwrite) int chaincount;
@property (nonatomic,readwrite) int chaingroup;
@property (nonatomic,readwrite) ccTime melttime;
@property (nonatomic,readwrite) ccTime falldelay;

+ (id) blockInGrid:(GridConfig*)grid pos:(CGPoint)gridpt color:(BlockColor)c shape:(BlockShape)s;
- (id) initInGrid:(GridConfig*)grid pos:(CGPoint)gridpt color:(BlockColor)c shape:(BlockShape)s;
- (void) refreshTexture;

- (void) glow;
- (void) animate;
- (void) makeTutorialBlock:(id)sender;
- (void) setCol:(BlockColor)c;
- (void) setShape:(BlockShape)s;
- (int) xSize;
- (int) ySize;

- (void) updateDisplay;

- (int) startMelt:(int)fallerchaincount chaingroup:(int)fallerchaingroup;
- (void) meltAnimation;
- (void) startFalling:(int)meltedchaincount chaingroup:(int)meltedchaingroup delay:(float)delay;

- (CGRect) gridRect;
- (CGRect) pxRect;
- (CGPoint) heartPos;  // Note: only gives one heart position for double blocks


@end

// Small trash icons appearing above the game field.
// Each trash Block contains a link to its TrashIcon (nil pointer if none) and
// vice versa.  All TrashIcons are also kept in the PuzzleStack's
// trashiconqueue_ array and displayed in trashicondisplay_.  Note that
// rightmost elements of trashiconqueue_ are at the beginning of the display
// and push other icons forward.

// Meanwhile, trash blocks not yet fallen are stored in trashqueue_.  Note that
// some blocks considered to be on the playing field (not in trashqueue_) are
// still in trashiconqueue_ because they are too high up to be visible to the
// player.
@interface TrashIcon : CCSprite {
	@public
	// Trash block on the trash queue or above the game field corresponding to this icon.
	Block* block_;

	// Chaingroup ID of the opposing player that originally created this unit of trash.  Used to synchronize trash dumps with end-of-chain.
	int chaingroup_;

	CCSprite* cancelslash_;
	bool cancelled_;
}

@property (assign,nonatomic,readwrite) Block* block_;

- (id) initWithBlock:(Block*)block chaingroup:(int)chaingroup;
@end

// Stats for an individual chaingroup.  Needed to compute overall stats due to the tree
// structure of chains.
@interface ChaingroupStats : NSObject {
	@public
	int numblocks_;
	int numchains_[MAX_CHAIN];

	// Info for burst of trash blocks also stored here
	int trashamounts_[3];
	CGPoint lastmeltpt_;
}
@end

// Puzzle stack abstract interface containing methods accessible by the opposing player.
@protocol PuzzleStackProtocol
// Receive this amount of trash from the other player.
- (void) receiveTrash:(int[3])amounts chaingroup:(int)chaingroup;
// Need to check the other player's stack to know when the trash can be unleashed on the current board.
- (bool) hasBlockWithChaingroup:(int)chaingroup;
// The loser tells the other player that he won.
- (void) winAnimation;
// Player finished selecting pre-game-start menu items.
- (bool) isReadyToStart;
// Is AI opponent?
- (bool) isAi;
// Place game elements on board and start raising the stack in preparation for start.
- (void) startRaiseIfReady;
// Mascot type this side?
- (Mascot) mascot;
// Hide side elements (for winning animation)
- (void) hideSideElements;
@end

// Max configuration of tiles, plus slack
#define GRID_XMAX (8+1)
#define GRID_YMAX 100
#define GRID_YMAXNOTRASH 20
#define GRIDTILES (GRID_XMAX*GRID_YMAX)

// Main puzzle stack object for one player, most of the game logic is in here.
@interface PuzzleStack : CCLayer <PuzzleStackProtocol, CCTargetedTouchDelegate>
{
	GridConfig* g_;

	// Main list of block objects on the board.
	NSMutableArray* blocks_;
	// Node containing all the blocks currently on the live stack (for offsetting).
	CCNode* blocksdisplay_;
	// Like blocksdisplay, except unaffected by raising
	CCNode* blocksdisplaybase_;

	// List of trash blocks yet to be dropped
	NSMutableArray* trashqueue_;
	bool exists_active_block_;  // is a block falling or melting in the current frame?
	int nexttrashside_;  // next trash to be dropped left or right?

	int heartscleared_;  // total number of hearts cleared so far.
	int heartsrecord_;  // all-time number of hearts cleared so far.
	int blockscleared_;
	int blockschained_;
	NSMutableDictionary* chaingroupstats_;
	int numcombos_[MAX_CHAIN];  // stats on the combos during this game.
	int numchains_[MAX_CHAIN];  // stats on the chains done during this game.
	int numlinesmanuallyraised_;  // number of lines player has raised by hand this game.
	ccTime lasttrashdroppedtime_;
	ccTime lastblockdroppedtime_;
	int numlandedthisframe_;

	// Trash icons for those not yet visible on board.
	NSMutableArray* trashiconqueue_;
	CCNode* trashicondisplay_;
	
	// Arrays of pointers to the stationary blocks in the puzzle grid, and their heart positions.  This is an alternate
	// representation of the game board (not containing falling/melting blocks) kept in sync with blocks_,
	// which is used to check for heart matches.
	Block* blockgrid_[GRIDTILES];
	int heartgrid_[GRIDTILES];

	// Whether a tile on the board is covered (this includes landed, falling and melting blocks).  This is
	// used to determine if a block can be placed there.
	bool covergrid_[GRIDTILES];

	// UI elements for placing a new block on the field.
	CCSprite* cursor_;  // basic cursor covering one tile
	Block* previewblock_;  // dim block preview on the board
	CGPoint starttouchpx_, spawnpointgrid_, currenttouchpx_;  // positions of the start/end touches
	BlockShape currentshape_;  // current directionality of the previewed block
	bool previewvalid_;

	// Block outline with hand icon going across it
	Block* tutorialblock_;
	
	// UI elements for two-finger scrolling
	UITouch* firsttouch;
	UITouch* secondtouch;
	bool cursorvalid_;
	bool touchactive_;
	bool secondtoucheveractive_;
	bool secondtouchactive_;
	CGPoint secondtouchstartpx_, secondtouchcurrentpx_;
	int distancescrolledpx_;

	// Next blocks available to the player
	int blocksplaced_;  // total number of blocks placed by the player so far.
	Block* currentblock_;  // current and next blocks being previewed on the side
	Block* nextblock_;
	CCSprite* background_;  // background of the board, tinted in the color of the current block
	CCSprite* slices_[8];  // colored bg slices
	CCSprite* foreground_;  // foreground overlay, to darken the whole board when necessary
	CCSprite* scrolldarkener_;  // overlay to darken scrolled part of board
	CCSprite* scrollarrows_[3]; // arrows showing scrolling
	CCSprite* dangerglow_;  // red glow when player at risk of dying
	TopClippedSprite* goalline_[2];  // Stack height dotted line
	TopClippedSprite* goallabel_[2];
	int nextline_; // next goalline array entry to use

	// Timer for autoraising, ai action
	ccTime raisecounter_, aicounter_;

	// Overall time since start of game
	ccTime gamecounter_;

	// Time since last placement, for displaying place hint.
	ccTime hintcounter_;

	// 3,2,1,dead
	int deathnum_;  // 3,2,1, or 0
	ccTime dangercounter_;  // time since "3"
	bool dangercountzero_;  // danger count "zero" finished flipping; trigger death
	CCSprite* dangersign_;  // graphical object showing counter
	bool isdyingduetofalling_;
	bool isdyingafterstabilize_;
	bool heartsbeating_;
	int alarmsoundid_;

	id<PuzzleStackProtocol> opponent_;  // other player's board; may be nil.
	PuzzleCommon* common_;  // shared state

	int raiselevel_;  // amount that the stack has been raised since the beginning
	int highestblocklevel_;  // like raiselevel, but relative to highest block rather than the base

	bool won_;  // has this player won the match?
	bool lost_; // lost the match?

	CCParticleWinRain* winemitter_[3];  // win confetti emitters
	
	CCTexture2D* mascottex_[4];
	CCTexture2D* fingerstex_[4];
	CCSprite* mascot_;
	CCSprite* fingers_; // foreground of mascot (fingers/"STEELBOT")
	CCSprite* shadow_;
	CCTexture2D* robotLEDtex_[10];
	CCTexture2D* maxLEDtex_[10];
	CCNode* robotLEDbox_; // contains both LEDs
	CCSprite* robotLED_[2];
	CCSprite* robotlogo_;
	CCSprite* robotfragments_[4];
	double roboshakemult_;
	bool placehint_;
	bool raisehint_;
	CCSprite* speechbubble_;
	CCLabel* speech_;
	CCNode* raisefingersbox_;
	CCSprite* raisefingers_[2];
	bool crouched_;
	bool robotmaxed_;
	int lednumber_;
	int rumblesound_;
	int bsodsound_;

	CCLabel* readylabel_;
	CCSprite* vacationsquare_;
	CCSprite* vacationlabel_;
	CCLabel* vacationtimelabel_;
	CCLabel* wins_;
	CCLabel* depthlabel_;
	CCLabel* winlabel_;
	CCLabel* winscount_;

	CCSprite* menubutton_;
	CCSprite* helpbutton_;
	CCSprite* pausebutton_;
	CCSprite* timerbg_;
	CCSprite* timerdigits_[5];
	const char* layoutname_;

	CCNode* linesreached_;
	CCLabel* linesreachedlabel_;
	CCLabel* sloweddownlabel_;
	CCLabel* newrecordlabel_;

	ccTime vacationtime_;

	// Initial menu items
	CCSprite* selectcharacter_;
	CCSprite* mascotheads_[NUM_MASCOTS];
	CCSprite* hat_;
	CCSprite* arrow_[NUM_MASCOTS];
	CCSprite* difficultyhighlight_;
	CCSprite* waitinghighlight_;
	bool showdifficulty_;
	CCSprite* handicaplabel_;
	CCSprite* difficulties_[NUM_DIFFICULTY];
	CCSprite* speedlabel_;
	CCSprite* speedarrowleft_;
	CCSprite* speedarrowright_;
	int maxdepthrounded_;
	int newselecteddepth_;
	int selecteddepth_;
	int displaylines_[3];
	SpeedClippedNode* speedbox_[3];
	CCLabel* speedlines_[3];
	CCLabel* speedspeed_[3];
	CCLabel* readybutton_;
	CCLabel* stealjob_;
	CCSprite* maxbotlevel_;
	CCSprite* maxnums_[2];

	IngameState ingamestate_;

	CCNode* statsview_;
	CCSprite* restartbutton_;
	CCSprite* endmenubutton_;

	CCSprite* centerbg_;
	CCSprite* centerbgpatch_; // to hide black background behind shake effect
	CCSprite* trashiconbg_;
}

@property (assign,nonatomic,readwrite) CCLabel* winlabel_;
@property (assign,nonatomic,readwrite) CCSprite* mascot_;
@property (assign,nonatomic,readwrite) bool won_;
@property (assign,nonatomic,readwrite) bool robotmaxed_;
@property (assign,nonatomic,readwrite) NSMutableArray* blocks_;
@property (assign,nonatomic,readwrite) id<PuzzleStackProtocol> opponent_;
@property (assign,nonatomic,readwrite) GridConfig* g_;

-(id) initWithGridConfig:(GridConfig*) grid common:(PuzzleCommon*) common;
-(void) dealloc;

// Set items from the menus
- (void) setMascot:(Mascot)mascot byPlayer:(bool)byplayer;
- (void) setDifficulty:(Difficulty)difficulty byPlayer:(bool)byplayer;

- (void) setSpeedLines:(id)sender;
- (void) moveSpeedLines:(bool)prev;

// Mini-game-loop for the animation of the stack raising at the beginning of a game.
- (void) initialRaiseAnimation: (ccTime) dt;
// Play countdown sound
- (void) playCountdown: (ccTime) dt;
// Display "WORK!" and unlock touch
- (void) startGame: (ccTime) dt;
// One iteration of the main game loop.  All the frame-by-frame logic is based here.
- (void) gameLoop: (ccTime) dt;
// Update timer label
- (void) updateTimer;
// Update background color for next block
- (void) updateBackgroundSlices;

// Finish the melting animation of 'block' and follow up with the consequences on the rest of the stack.
- (void) endMelt:(Block*) block;
// Check the whole stack for any unsupported blocks, and make them start falling.  The newly fallen blocks
// will receive 'chaincount' (which should be highest of the chaincounts of the melted block(s) that made them
// start falling) and 'chaingroup' (unique id identifying chain).
- (void) checkUnsupported:(int) chaincount chaingroup:(int) chaingroup;
// If 'block' is overlapping with any other blocks, make it land.
- (void) tryLand:(Block*) block;
// Bring the landed-block grid arrays in sync with the blocks_ list.
- (void) syncGrid;
// Check for any heart matches among landed blocks, and make them start melting if so.
- (void) testMatches;
// Check for already melting block around trash that just fell.
- (void) checkAroundTrash:(Block*) t;
// Add blocks from one level of the generated stack to the active playing field.
- (void) addBlocksAtRaiseLevel:(int)level;
// Line with number showing lines reached so far.
- (void) addGoalLine:(int)level;
// Raise the field by one unit.
- (void) raiseField:(ccTime)risedelay;
// How much time per one unit of raising?  Accelerates over the course of a game.
- (ccTime) raiseTime:(int)depth;
// Add vacation time equivalent to 'points' units of trash, with diminishing returns.
- (void) addVacationTime:(int)points;
// Update vacation string display.
- (void) setVacationString;
// Update 'lines deep' display.
- (void) setDepthString;

// Create combo circle icon.
+ (CCSprite*) comboCircleWithCount:(int)count pos:(CGPoint)pos;
// Create spinning star sprite.
+ (CCSprite*) chainStarWithCount:(int)c start:(CGPoint)starstart end:(CGPoint)jumpend;
// Determine where to put the circle or star relative to the blocks that triggered it (in blocksdisplay_ space).
+ (CGPoint) iconPosIsCombo:(bool)iscombo block1:(Block*)b1 block2:(Block*)b2 pxxsize:(int)pxxsize pxysize:(int)pxysize;

// Trash icon burst from cleared block
- (void) trashBurst:(CGPoint)pos trashamounts:(int[3])trashamounts iscombo:(bool)iscombo;

// Score a combo or chain of strength 'count', displaying at the point of a match between b1 and b2.
- (void) doAttack:(int)count chaingroup:(int)chaingroup iscombo:(bool)iscombo b1:(Block*)b1 b2:(Block*)b2;
// Throw a new block onto the board, as initiated by the player.
- (void) throwBlock;
// Update block position at the end of the Cocos block throwing action.
- (void) endPlace:(id)block;
// Make the next block ready to throw.
- (void) rotateBlockColor;

// Aggregate and remove groups from the chaingroupstats_ object.
- (void) computeChainStats;
// Turn the trash icons into real trash on the board.
- (void) dropTrashFromQueue;
// Cancel trash icon if removed by combo
- (void) cancelTrashForIcon:(TrashIcon*)icon;
// Indicate it's safe to remove trash icon now that 'block' is visible on the board (and not above).
- (void) dropTrashIconForBlock:(Block*)block;
// Reposition trash icons after some have been added/removed.
- (void) rearrangeTrashIcons;

// Is a block touching the top of the screen?
- (bool) isDying:(float)offset;
// Will a block still be touching the top of the screen after all current melts and falls are done?
- (bool) predictDyingAfterStabilize;
// Increment 3,2,1 countdown
- (void) incrementDangerCounter:(ccTime)dt;
// Flip to next count animation.
- (void) swapDangerSign:(id)sender;
// Set after zero count finished flipping.
- (void) dangerCountZero:(id)sender;
// Start the next alarm loop sound.
- (void) alarmSoundCallback:(ccTime)dt;
// Game over.  Make this player lose.
- (void) die;
// Game over.  Make this winning player stop playing.
- (void) terminateWin;

// Make visible the preview block that the player is currently touching.
- (void) placePreview;
// Make the mascot crouch.
- (void) crouch;
// Make the mascot return to standing position (from a crouch, without throwing).
- (void) uncrouch;
// Is the preview going through an existing block?
- (void) checkPreviewValid;
// The player let go of the touchscreen, place the preview block if appropriate.
- (void) endTouch;
// Interpret touch in the pre-game screen.
- (void) mascotSelectTouch:(UITouch *)touch newTouch:(bool)newtouch;
// End drag movement
- (void) snapStackToTouch;
// Convert touch position to grid position.
- (CGPoint) pxToGrid:(CGPoint)pxpt;

// To be used at the end of some Cocos actions.
- (void) killTrashIcon:(id)sender;
- (void) winAnimation2:(id)sender;

// Time to next AI action.
- (ccTime) aiTime;
// Make the AI throw his next block.  Also reused for tutorial block outline
// placement.
- (Block*) aiPlay:(bool)tutorialmode;

- (void) placeTutorialBlock;
- (void) removeTutorialBlock;

// Create end-of-match stats text
- (void) createStatsView;

- (BOOL) checkSideButtons:(UITouch*) touch;
- (BOOL) checkStatsButtons:(UITouch*) touch;

- (void) pause;
- (void) unpause;

- (void) destroyRobotAnimation;
- (void) ledCallback:(ccTime)dt;
- (void) shakeCallback:(ccTime)dt;
- (void) bsodCallback:(ccTime)dt;
- (void) explodeRobot:(id)sender;
- (void) bangSound:(id)sender;
- (void) clingSound:(id)sender;

- (void) setRobotLED:(int)displayno;

- (void) speechBubble:(NSString*)chatkey;

- (void) showWins;

@end

